// app/business/api/common/exchange.go
package common

import (
	"context"
	"fmt"
	"math/big"
	"strings"
	"time"

	"github.com/ethereum/go-ethereum/accounts/abi"
	"github.com/ethereum/go-ethereum/accounts/abi/bind"
	"github.com/ethereum/go-ethereum/common"
	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/crypto"
	"github.com/ethereum/go-ethereum/ethclient"

	"private-domain-overseas-service/pkg/constant"
)

// TransferConfig 转账配置
type TransferConfig struct {
	Client             *ethclient.Client
	HzbContractAddress string
	PrivateKey         string
	From               common.Address
	To                 common.Address
	Amount             *big.Int
}

// ExchangeToken 执行代币转账
// 参数说明：
// - config: 转账配置
// 返回：
// - *constant.ExchangeResult: 转账结果
// - error: 错误信息
func ExchangeToken(config *TransferConfig) (*constant.ExchangeResult, error) {
	// 验证输入
	if config.To == (common.Address{}) {
		return nil, fmt.Errorf("invalid receiver address: zero address")
	}
	if config.Amount == nil || config.Amount.Sign() <= 0 {
		return nil, fmt.Errorf("invalid amount: must be positive")
	}

	// 解析 HZB 合约 ABI
	hzbABI, err := abi.JSON(strings.NewReader(constant.HzbABI))
	if err != nil {
		return nil, fmt.Errorf("failed to parse HZB ABI: %v", err)
	}

	// 获取链 ID (zkSync Era testnet: 280)
	chainID, err := config.Client.NetworkID(context.Background())
	if err != nil {
		return nil, fmt.Errorf("failed to get chain ID: %v", err)
	}

	// 解析私钥
	privateKeyECDSA, err := crypto.HexToECDSA(config.PrivateKey)
	if err != nil {
		return nil, fmt.Errorf("invalid private key: %v", err)
	}

	// 创建交易者
	auth, err := bind.NewKeyedTransactorWithChainID(privateKeyECDSA, chainID)
	if err != nil {
		return nil, fmt.Errorf("failed to create transactor: %v", err)
	}

	// 检查发送者余额
	balance, err := checkBalance(config.Client, config.HzbContractAddress, hzbABI, config.From)
	if err != nil {
		return nil, fmt.Errorf("failed to check sender balance: %v", err)
	}
	if balance.Cmp(config.Amount) < 0 {
		return nil, fmt.Errorf("insufficient balance: have %v, need %v", balance, config.Amount)
	}

	// 预测 gas 费用
	gasEstimate, err := EstimateGasFee(config.Client, config.HzbContractAddress, config.From, config.To, config.Amount)
	if err != nil {
		return nil, fmt.Errorf("failed to estimate gas fee: %v", err)
	}
	auth.GasPrice = gasEstimate.GasPrice
	auth.GasLimit = gasEstimate.GasLimit.Uint64()

	// 构造转账交易
	data, err := hzbABI.Pack("transfer", config.To, config.Amount)
	if err != nil {
		return nil, fmt.Errorf("failed to pack transfer data: %v", err)
	}

	nonce, err := config.Client.PendingNonceAt(context.Background(), config.From)
	if err != nil {
		return nil, fmt.Errorf("failed to get nonce: %v", err)
	}

	tx := types.NewTransaction(
		nonce,
		common.HexToAddress(config.HzbContractAddress),
		big.NewInt(0),
		auth.GasLimit,
		auth.GasPrice,
		data,
	)

	// 签名交易
	signedTx, err := auth.Signer(config.From, tx)
	if err != nil {
		return nil, fmt.Errorf("failed to sign transaction: %v", err)
	}

	// 发送交易
	err = config.Client.SendTransaction(context.Background(), signedTx)
	if err != nil {
		return nil, fmt.Errorf("failed to send transaction: %v", err)
	}

	// 等待交易确认（带超时）
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute)
	defer cancel()
	receipt, err := bind.WaitMined(ctx, config.Client, signedTx)
	if err != nil {
		return &constant.ExchangeResult{
			TxHash:  signedTx.Hash().Hex(),
			Success: false,
		}, fmt.Errorf("failed to confirm transaction: %v", err)
	}

	// 计算 Gas 费用
	gasUsed := big.NewInt(int64(receipt.GasUsed))
	gasFee := new(big.Int).Mul(gasUsed, gasEstimate.GasPrice)
	gasFeeHzb := new(big.Int).Div(
		new(big.Int).Mul(gasFee, gasEstimate.EthToHzbRate),
		big.NewInt(1e18),
	)

	return &constant.ExchangeResult{
		TxHash:    signedTx.Hash().Hex(),
		GasUsed:   gasUsed,
		GasFee:    gasFee,
		GasFeeHzb: gasFeeHzb,
		Success:   receipt.Status == 1,
	}, nil
}

// checkBalance 查询地址的代币余额
func checkBalance(client *ethclient.Client, contractAddress string, hzbABI abi.ABI, address common.Address) (*big.Int, error) {
	contract := bind.NewBoundContract(common.HexToAddress(contractAddress), hzbABI, client, client, client)

	// 创建调用选项
	opts := &bind.CallOpts{
		Context: context.Background(),
	}

	// 调用合约方法
	var result []interface{}
	err := contract.Call(opts, &result, "balanceOf", address)
	if err != nil {
		return nil, err
	}

	// 转换结果
	if len(result) == 0 {
		return nil, fmt.Errorf("no balance returned")
	}

	balance, ok := result[0].(*big.Int)
	if !ok {
		return nil, fmt.Errorf("invalid balance type")
	}

	return balance, nil
}
